/*jslint sub: true */
/*global os, fs, path, cli, spawn, jsdom, require, setTimeout, Deferred, DEBUGGER */

/**
 * Пространство имен шаблонов.
 *
 * @namespace
 * @name Template
 */
var Template = {};

(function () {
    'use strict';

    var lock = false;

    /**
     * Объект с шаблонами.
     *
     * @public
     * @field
     * @name Template.templates
     * @type {Object}
     */
    Template.templates = null;

    /**
     * Получить содержимое шаблона.
     *
     * @public
     * @function
     * @name Template.fetch
     * @param {String} template Путь к шаблону.
     * @param {Object} [options] Параметры вызова.
     * @return {String}
     */
    Template.fetch = function (template, options) {
        var object  = this.templates || {},
            message = 'Шаблон #{template} не найден!';
        // Преобразуем к строке название шаблона.
        template = String(template || '');
        // Проверяем название шаблона, должно быть соответствие по шаблону пути.
        if (!/^[a-z](-?[0-9a-z])*(\/[a-z](-?[0-9a-z])*)*$/.test(template)) {
            throw new TypeError('Название шаблона задано не верно, ожидается строка формата /^[a-z](-?[0-9a-z])*(\/[a-z](-?[0-9a-z])*)*$/.');
        }
        // Переводим все символы underscore в символы CamelCase.
        template.replace(/-[a-z0-9]/g, function (matches) {
            return matches.substring(1).toUpperCase();
        });
        // Производим поиск обрабатывающей функции.
        template.split('/').forEach(function (element) {
            var elementType = typeof object[element];
            if (elementType === 'undefined') {
                // Выбрасываем исключение, что шаблон не найден.
                throw new TypeError(message.split('#{template}').
                    join(JSON.stringify(template)));
            }
            object = object[element];
        });
        // Проверяем, что найденный обработчик является функцией.
        if (typeof object !== 'function') {
            // Выбрасываем исключение, что шаблон не найден.
            throw new TypeError(message.split('#{template}').
                join(JSON.stringify(template)));
        }
        // Получаем текстовый контент.
        return object(options || {});
    };

    /**
     * Компилирование шаблонов.
     *
     * @public
     * @function
     * @name Template.compile
     * @param {Object} options Параметры компиляции шаблонов.
     * @param {String} options.filename Относительный путь от временной директории к файлу назначения.
     * @param {String} options.closureTemplates Относительный путь от корня проекта к компилятору шаблонов.
     * @param {String} options.closureCompiler Относительный путь от корня проекта к компилятору javascript.
     * @param {String} options.sources Относительный путь от корня проекта к директории шаблонов.
     * @param {String} options.temp Относительный путь от корня проекта к директории временных файлов.
     * @param {String} [options.namespace='defaults'] Пространство имен шаблонов.
     * @param {String[]} [options.files] Список которые будут использоваться для компиляции, кроме этих указанных
     *        файлов компилироваться больше ничего не будет.
     * @param {Boolean} [options.eraseTemporaryFiles=false]
     * @param {Function} callback Обработчик окончания компиляции.
     * @return {void}
     */
    Template.compile = function (options, callback) {
        options = options || {};
        var identifiers = [],                 // Список идентификаторов сообщений.
            filenames   = [],                 // Список файлов для компиляции.
            timestamp   = Number(new Date()), // Метка времени начала компиляции.
            cache       = false,              // Признак использования кеша.

            output,                  // Путь к выходному файлу.
            files = [],              // Список файлов для компиляции.
            globals = [],            // Список глобальных переменных.
            sourceLocale,            // Локализация файлов шаблонов.
            targetLocale,            // Локализация выходного файла.
            useCleaning = !DEBUGGER, // Признак автоматической очистки временных файлов.
            useDebugging = false,    // Признак использования отладки.
            priorities  = [],        // Список локализаций приоритетов.
            baseDirectory,           // Базовая директория проекта.
            sourcesDirectory,        // Директория шаблонов.
            messagesDirectory,       // Директория переводов.
            tempDirectory,           // Директория временных файлов.
            closureCompiler,         // Путь к компилятору SoyToJsSrcCompiler.
            closureTemplates,        // Путь к компилятору SoyToJsSrcCompiler.
            closureMsgExtractor,     // Путь к компилятору SoyMsgExtractor.

            sourcesSkeleton = [],    // Скелет исходных шаблонов.
            directoriesLocales = [], // Директории переводов.

            difference = function (start, end) {
                var startTimestamp = Number(start),
                    endTimestamp = Number(end),
                    difference = ('  ' + String((endTimestamp - startTimestamp) / 1000 + 0.0001)).split('.'),
                    major = difference[0].substring(-3),
                    minor = difference[1].substring(0, 3);
                return major + '.' + minor + ' sec: ';
            },

            filename    = String(options['filename'] || ''),     // Название файла, в который будет производится компиляция.
            section     = String(options['section'] || 'views'), // Название секции в конфигурации в которой хранятся настройки.
            namespace,                                           // Простраство имен шаблонов.
            source,                                              // Директория с исходными файлами шаблонов.
            temp;                                                // Временная папка для компиляции.

        Deferred.$create([
            // ---------------------------------------------------------------------------------------------------------
            // Параметр автоочистки временных файлов.
            function (success) {
                var optionType = typeof options['useCleaning'];
                if (optionType === 'string') {
                    useCleaning = ['true', 'yes', 'on', '1'].indexOf(options['useCleaning']) !== -1;
                } else if (optionType === 'number') {
                    useCleaning = options['useCleaning'] > 0;
                } else if (optionType !== 'undefined') {
                    useCleaning = !!options['useCleaning'];
                }
                success();
            },
            // Параметр использования отладчика.
            function (success) {
                var optionType = typeof options['useDebugging'];
                if (optionType === 'string') {
                    useDebugging = ['true', 'yes', 'on', '1'].indexOf(options['useDebugging']) !== -1;
                } else if (optionType === 'number') {
                    useDebugging = options['useDebugging'] > 0;
                } else if (optionType !== 'undefined') {
                    useDebugging = !!options['useDebugging'];
                }
                success();
            },
            // ---------------------------------------------------------------------------------------------------------
            // Блокировка процесса компиляции и продолжение после
            // того, как освободятся все процессы компиляции.
            function (success) {
                var callback = function () {
                    if (lock) {
                        setTimeout(callback, 100);
                    } else {
                        lock = true;
                        if (DEBUGGER && useDebugging) {
                            // Выводим отладочную информацию в консоль.
                            cli.debug('==> Compiling templates');
                        }
                        success();
                    }
                };
                callback();
            },
            // ---------------------------------------------------------------------------------------------------------
            // Проверяем, что задана директория baseDirectory.
            function (success, failure) {
                if (typeof options['baseDirectory'] !== 'string') {
                    // Путь к директории не является строкой.
                    failure('Not configured required option "baseDirectory"!');
                } else {
                    // Сохраняем путь в локальной переменной.
                    baseDirectory = options['baseDirectory'];
                    success();
                }
            },
            // Ищем абсолютный путь к директории.
            function (success, failure) {
                var time = new Date();
                fs.realpath(baseDirectory, function (error, resolved) {
                    if (DEBUGGER && useDebugging) {
                        // Выводим отладочную информацию в консоль.
                        cli.debug(difference(time, new Date()) + 'fs.realpath: ' + JSON.stringify(baseDirectory));
                    }
                    if (error) {
                        // Путь к компилятору не найден или доступ запрещен.
                        failure('Path ' + JSON.stringify(baseDirectory) + ' not found or access denied!');
                    } else {
                        baseDirectory = resolved;
                        success();
                    }
                });
            },
            // Проверяем, что параметр указывает на директорию.
            function (success, failure) {
                var time = new Date();
                fs.stat(baseDirectory, function (error, stats) {
                    if (DEBUGGER && useDebugging) {
                        // Выводим отладочную информацию в консоль.
                        cli.debug(difference(time, new Date()) + 'fs.stat: ' + JSON.stringify(baseDirectory));
                    }
                    if (error) {
                        failure(error);
                    } else if (stats && !stats.isDirectory()) {
                        // Компилятор не является директорией.
                        failure('Path ' + JSON.stringify(baseDirectory) + ' is not a directory!');
                    } else {
                        success();
                    }
                });
            },
            // ---------------------------------------------------------------------------------------------------------
            // Проверяем, что задана директория sourcesDirectory.
            function (success, failure) {
                var optionType = typeof options['sourcesDirectory'];
                if (optionType !== 'undefined' && optionType !== 'string') {
                    // Путь к директории не является строкой.
                    failure('Option \'sourcesDirectory\' is not a string!');
                } else if (optionType === 'string') {
                    // Переопределяем значение по умолчанию.
                    sourcesDirectory = path.join(baseDirectory, options['sourcesDirectory']);
                    success();
                } else {
                    // Значение по умолчанию.
                    sourcesDirectory = path.join(baseDirectory, 'views/sources');
                    success();
                }
            },
            // Ищем абсолютный путь к директории.
            function (success, failure) {
                var time = new Date();
                fs.realpath(sourcesDirectory, function (error, resolved) {
                    if (DEBUGGER && useDebugging) {
                        // Выводим отладочную информацию в консоль.
                        cli.debug(difference(time, new Date()) + 'fs.realpath: ' + JSON.stringify(sourcesDirectory));
                    }
                    if (error) {
                        // Путь к компилятору не найден или доступ запрещен.
                        failure('Path ' + JSON.stringify(sourcesDirectory) + ' not found or access denied!');
                    } else {
                        sourcesDirectory = resolved;
                        success();
                    }
                });
            },
            // Проверяем, что параметр указывает на директорию.
            function (success, failure) {
                var time = new Date();
                fs.stat(sourcesDirectory, function (error, stats) {
                    if (DEBUGGER && useDebugging) {
                        // Выводим отладочную информацию в консоль.
                        cli.debug(difference(time, new Date()) + 'fs.stat: ' + JSON.stringify(sourcesDirectory));
                    }
                    if (error) {
                        failure(error);
                    } else if (stats && !stats.isDirectory()) {
                        // Компилятор не является директорией.
                        failure('Path ' + JSON.stringify(sourcesDirectory) + ' is not a directory!');
                    } else {
                        success();
                    }
                });
            },
            // ---------------------------------------------------------------------------------------------------------
            // Проверяем, что задана директория messagesDirectory.
            function (success, failure) {
                var optionType = typeof options['messagesDirectory'];
                if (optionType !== 'undefined' && optionType !== 'string') {
                    // Путь к директории не является строкой.
                    failure('Option \'messagesDirectory\' is not a string!');
                } else if (optionType === 'string') {
                    // Переопределяем значение по умолчанию.
                    messagesDirectory = path.join(baseDirectory, options['messagesDirectory']);
                    success();
                } else {
                    // Значение по умолчанию.
                    messagesDirectory = path.join(baseDirectory, 'views/messages');
                    success();
                }
            },
            // Ищем абсолютный путь к директории.
            function (success, failure) {
                var time = new Date();
                fs.realpath(messagesDirectory, function (error, resolved) {
                    if (DEBUGGER && useDebugging) {
                        // Выводим отладочную информацию в консоль.
                        cli.debug(difference(time, new Date()) + 'fs.realpath: ' + JSON.stringify(messagesDirectory));
                    }
                    if (error) {
                        // Путь к компилятору не найден или доступ запрещен.
                        failure('Path ' + JSON.stringify(messagesDirectory) + ' not found or access denied!');
                    } else {
                        messagesDirectory = resolved;
                        success();
                    }
                });
            },
            // Проверяем, что параметр указывает на директорию.
            function (success, failure) {
                var time = new Date();
                fs.stat(messagesDirectory, function (error, stats) {
                    if (DEBUGGER && useDebugging) {
                        // Выводим отладочную информацию в консоль.
                        cli.debug(difference(time, new Date()) + 'fs.stat: ' + JSON.stringify(messagesDirectory));
                    }
                    if (error) {
                        failure(error);
                    } else if (stats && !stats.isDirectory()) {
                        // Компилятор не является директорией.
                        failure('Path ' + JSON.stringify(messagesDirectory) + ' is not a directory!');
                    } else {
                        success();
                    }
                });
            },
            // ---------------------------------------------------------------------------------------------------------
            // Проверяем, что задана директория tempDirectory.
            function (success, failure) {
                var optionType = typeof options['tempDirectory'];
                if (optionType !== 'undefined' && optionType !== 'string') {
                    // Путь к директории не является строкой.
                    failure('Option \'tempDirectory\' is not a string!');
                } else if (optionType === 'string') {
                    // Переопределяем значение по умолчанию.
                    tempDirectory = path.join(baseDirectory, options['tempDirectory']);
                    success();
                } else {
                    // Значение по умолчанию.
                    tempDirectory = os.tmpdir();
                    success();
                }
            },
            // Ищем абсолютный путь к директории.
            function (success, failure) {
                var time = new Date();
                fs.realpath(tempDirectory, function (error, resolved) {
                    if (DEBUGGER && useDebugging) {
                        // Выводим отладочную информацию в консоль.
                        cli.debug(difference(time, new Date()) + 'fs.realpath: ' + JSON.stringify(tempDirectory));
                    }
                    if (error) {
                        // Путь к компилятору не найден или доступ запрещен.
                        failure('Path ' + JSON.stringify(tempDirectory) + ' not found or access denied!');
                    } else {
                        tempDirectory = resolved;
                        success();
                    }
                });
            },
            // Проверяем, что параметр указывает на директорию.
            function (success, failure) {
                var time = new Date();
                fs.stat(tempDirectory, function (error, stats) {
                    if (DEBUGGER && useDebugging) {
                        // Выводим отладочную информацию в консоль.
                        cli.debug(difference(time, new Date()) + 'fs.stat: ' + JSON.stringify(tempDirectory));
                    }
                    if (error) {
                        failure(error);
                    } else if (stats && !stats.isDirectory()) {
                        // Компилятор не является директорией.
                        failure('Path ' + JSON.stringify(tempDirectory) + ' is not a directory!');
                    } else {
                        success();
                    }
                });
            },
            // ---------------------------------------------------------------------------------------------------------
            // Проверяем, что задан параметр closureCompiler.
            function (success, failure) {
                var optionType = typeof options['closureCompiler'];
                if (optionType !== 'undefined' && optionType !== 'string') {
                    // Путь к компилятору задан не строкой.
                    failure('Option \'closureCompiler\' is not a string!');
                } else if (optionType === 'string') {
                    // Переопределяем значение по умолчанию.
                    closureCompiler = path.join(baseDirectory, options['closureCompiler']);
                    success();
                } else {
                    // Значение по умолчанию.
                    closureCompiler = path.join(baseDirectory, 'java/closure-compiler.jar');
                    success();
                }
            },
            // Ищем абсолютный путь к компилятору.
            function (success, failure) {
                var time = new Date();
                fs.realpath(closureCompiler, function (error, resolved) {
                    if (DEBUGGER && useDebugging) {
                        // Выводим отладочную информацию в консоль.
                        cli.debug(difference(time, new Date()) + 'fs.realpath: ' + JSON.stringify(closureCompiler));
                    }
                    if (error) {
                        // Путь к компилятору не найден или доступ запрещен.
                        failure('Path ' + JSON.stringify(closureCompiler) + ' not found or access denied!');
                    } else {
                        closureCompiler = resolved;
                        success();
                    }
                });
            },
            // Проверяем, что компилятор является файлом.
            function (success, failure) {
                var time = new Date();
                fs.stat(closureCompiler, function (error, stats) {
                    if (DEBUGGER && useDebugging) {
                        // Выводим отладочную информацию в консоль.
                        cli.debug(difference(time, new Date()) + 'fs.stat: ' + JSON.stringify(closureCompiler));
                    }
                    if (error) {
                        failure(error);
                    } else if (stats && !stats.isFile()) {
                        // Компилятор не является файлом.
                        failure('Path ' + JSON.stringify(closureCompiler) + ' is not a file!');
                    } else {
                        success();
                    }
                });
            },
            // ---------------------------------------------------------------------------------------------------------
            // Проверяем, что задан параметр closureTemplates.
            function (success, failure) {
                var optionType = typeof options['closureTemplates'];
                if (optionType !== 'undefined' && optionType !== 'string') {
                    // Путь к компилятору задан не строкой.
                    failure('Option \'closureTemplates\' is not a string!');
                } else if (optionType === 'string') {
                    // Переопределяем значение по умолчанию.
                    closureTemplates = path.join(baseDirectory, options['closureTemplates']);
                    success();
                } else {
                    // Значение по умолчанию.
                    closureTemplates = path.join(baseDirectory, 'java/closure-templates.jar');
                    success();
                }
            },
            // Ищем абсолютный путь к компилятору.
            function (success, failure) {
                var time = new Date();
                fs.realpath(closureTemplates, function (error, resolved) {
                    if (DEBUGGER && useDebugging) {
                        // Выводим отладочную информацию в консоль.
                        cli.debug(difference(time, new Date()) + 'fs.realpath: ' + JSON.stringify(closureTemplates));
                    }
                    if (error) {
                        // Путь к компилятору не найден или доступ запрещен.
                        failure('Path ' + JSON.stringify(closureTemplates) + ' not found or access denied!');
                    } else {
                        closureTemplates = resolved;
                        success();
                    }
                });
            },
            // Проверяем, что компилятор является файлом.
            function (success, failure) {
                var time = new Date();
                fs.stat(closureTemplates, function (error, stats) {
                    if (DEBUGGER && useDebugging) {
                        // Выводим отладочную информацию в консоль.
                        cli.debug(difference(time, new Date()) + 'fs.stat: ' + JSON.stringify(closureTemplates));
                    }
                    if (error) {
                        failure(error);
                    } else if (stats && !stats.isFile()) {
                        // Компилятор не является файлом.
                        failure('Path ' + JSON.stringify(closureTemplates) + ' is not a file!');
                    } else {
                        success();
                    }
                });
            },
            // ---------------------------------------------------------------------------------------------------------
            // Проверяем, что задан параметр closureMsgExtractor.
            function (success, failure) {
                var optionType = typeof options['closureMsgExtractor'];
                if (optionType !== 'undefined' && optionType !== 'string') {
                    // Путь к компилятору задан не строкой.
                    failure('Option \'closureMsgExtractor\' is not a string!');
                } else if (optionType === 'string') {
                    // Переопределяем значение по умолчанию.
                    closureMsgExtractor = path.join(baseDirectory, options['closureMsgExtractor']);
                    success();
                } else {
                    // Значение по умолчанию.
                    closureMsgExtractor = path.join(baseDirectory, 'java/closure-msg-extractor.jar');
                    success();
                }
            },
            // Ищем абсолютный путь к компилятору.
            function (success, failure) {
                var time = new Date();
                fs.realpath(closureMsgExtractor, function (error, resolved) {
                    if (DEBUGGER && useDebugging) {
                        // Выводим отладочную информацию в консоль.
                        cli.debug(difference(time, new Date()) + 'fs.realpath: ' + JSON.stringify(closureMsgExtractor));
                    }
                    if (error) {
                        // Путь к компилятору не найден или доступ запрещен.
                        failure('Path ' + JSON.stringify(closureMsgExtractor) + ' not found or access denied!');
                    } else {
                        closureMsgExtractor = resolved;
                        success();
                    }
                });
            },
            // Проверяем, что компилятор является файлом.
            function (success, failure) {
                var time = new Date();
                fs.stat(closureMsgExtractor, function (error, stats) {
                    if (DEBUGGER && useDebugging) {
                        // Выводим отладочную информацию в консоль.
                        cli.debug(difference(time, new Date()) + 'fs.stat: ' + JSON.stringify(closureMsgExtractor));
                    }
                    if (error) {
                        failure(error);
                    } else if (stats && !stats.isFile()) {
                        // Компилятор не является файлом.
                        failure('Path ' + JSON.stringify(closureMsgExtractor) + ' is not a file!');
                    } else {
                        success();
                    }
                });
            },
            // ---------------------------------------------------------------------------------------------------------
            // Проверяем, что задан параметр output.
            function (success, failure) {
                if (typeof options['output'] !== 'string') {
                    // Останавливаем процесс компиляции с ошибкой.
                    failure('Not configured output file (option \'output\')!');
                } else {
                    output = path.join(baseDirectory, options['output'] + '.js');
                    success();
                }
            },
            // Проверяем, что задана локализация исходников.
            function (success, failure) {
                if (typeof options['sourceLocale'] !== 'string' || !options['sourceLocale']) {
                    // Останавливаем процесс компиляции с ошибкой.
                    failure('Not configured source locale string (option \'sourceLocale\')!');
                } else if (!/^[a-z]{2,2}\-[A-Z]{2,2}$/.test(options['sourceLocale'])) {
                    // Останавливаем процесс компиляции с ошибкой.
                    failure('Source locale string ' + JSON.stringify(options['sourceLocale']) + ' does not match the regular expression /^[a-z]{2,2}\\-[A-Z]{2,2}$/.');
                } else {
                    sourceLocale = options['sourceLocale'];
                    success();
                }
            },
            // Проверяем, что задана локализация компиляции.
            function (success, failure) {
                if (typeof options['targetLocale'] !== 'string' || !options['targetLocale']) {
                    // Останавливаем процесс компиляции с ошибкой.
                    failure('Not configured target locale string (option \'targetLocale\')!');
                } else if (!/^[a-z]{2,2}\-[A-Z]{2,2}$/.test(options['targetLocale'])) {
                    // Останавливаем процесс компиляции с ошибкой.
                    failure('Target locale string ' + JSON.stringify(options['targetLocale']) + ' does not match the regular expression /^[a-z]{2,2}\\-[A-Z]{2,2}$/.');
                } else {
                    targetLocale = options['targetLocale'];
                    success();
                }
            },
            // Проверяем правильнось задания приоритетов.
            function (success, failure) {
                var type  = typeof options['priorities'],
                    list  = options['priorities'],
                    error = '';
                // Приводим строку к массиву.
                if (type === 'string') {
                    list = list.split(',');
                }
                if (list instanceof Array) {
                    // Перебираем массив и проверяем локализации.
                    list.forEach(function (element) {
                        if (!error && (typeof element !== 'string' || !/^[a-z]{2,2}\-[A-Z]{2,2}$/.test(element))) {
                            error = 'Locale priority string ' + JSON.stringify(element) + ' does not match the regular expression /^[a-z]{2,2}\\-[A-Z]{2,2}$/.';
                        } else if (!error && priorities.indexOf(element) === -1 && element !== targetLocale && element !== sourceLocale) {
                            priorities.push(element);
                        }
                    });
                    if (error) {
                        failure(error);
                    } else {
                        priorities.push(targetLocale);
                        success();
                    }
                } else if (type !== 'undefined') {
                    failure('Defined incorrectly option \'priorities\'!');
                } else {
                    priorities.push(targetLocale);
                    success();
                }
            },
            // Проверяем правильность задания
            // списка файлов для компиляции.
            function (success, failure) {
                var type  = typeof options['files'],
                    list  = options['files'],
                    error = '';
                // Приводим строку к массиву.
                if (type === 'string') {
                    list = list.split(',');
                }
                if (list instanceof Array) {
                    // Перебираем массив и проверяем файлы.
                    list.forEach(function (element) {
                        if (!error && (typeof element !== 'string' || !element)) {
                            error = 'Filename ' + JSON.stringify(element) + ' is incorrect.';
                        } else if (!error && files.indexOf(element) === -1) {
                            files.push(element);
                        }
                    });
                    if (error) {
                        failure(error);
                    } else {
                        success();
                    }
                } else if (type !== 'undefined') {
                    failure('Defined incorrectly option \'files\'!');
                } else {
                    success();
                }
            },
            // Выводим параметры в отладку.
            function (success) {
                if (DEBUGGER && useDebugging) {
                    cli.debug('        baseDirectory       : ' + JSON.stringify(baseDirectory));
                    cli.debug('        sourcesDirectory    : ' + JSON.stringify(sourcesDirectory));
                    cli.debug('        messagesDirectory   : ' + JSON.stringify(messagesDirectory));
                    cli.debug('        tempDirectory       : ' + JSON.stringify(tempDirectory));
                    cli.debug('        closureCompiler     : ' + JSON.stringify(closureCompiler));
                    cli.debug('        closureTemplates    : ' + JSON.stringify(closureTemplates));
                    cli.debug('        closureMsgExtractor : ' + JSON.stringify(closureMsgExtractor));
                    cli.debug('        sourceLocale        : ' + JSON.stringify(sourceLocale));
                    cli.debug('        targetLocale        : ' + JSON.stringify(targetLocale));
                    cli.debug('        priorities          : ' + JSON.stringify(priorities));
                    cli.debug('        useCleaning         : ' + JSON.stringify(useCleaning));
                    cli.debug('        output              : ' + JSON.stringify(output));
                    cli.debug('        files               : ' + JSON.stringify(files));
                }
                success();
            },
            // ---------------------------------------------------------------------------------------------------------
            // Создаем директорию выходного файла.
            function (success, failure) {
                if (DEBUGGER && useDebugging) {
                    // Выводим отладочную информацию в консоль.
                    cli.debug('====> Create output directory');
                }
                var time = new Date(),
                    deferred = Deferred.$create([], function (error) {
                        if (DEBUGGER && useDebugging) {
                            // Выводим отладочную информацию в консоль.
                            cli.debug(difference(time, new Date()) + 'Complete!');
                        }
                        if (error) {
                            failure(error['message']);
                        } else {
                            success();
                        }
                    });
                // Перебираем все части пути и создаем
                // директории, если они еще не созданы.
                path.dirname(output).split(path.sep).
                    forEach(function (element, index, array) {
                        var directory = array.slice(0, index + 1).join(path.sep),
                            exists = true;
                        if (directory) {
                            // Проверяем существование директории.
                            deferred.add(function (success, failure) {
                                var time = new Date();
                                fs.stat(directory, function (error, stats) {
                                    if (DEBUGGER && useDebugging) {
                                        // Выводим отладочную информацию в консоль.
                                        cli.debug(difference(time, new Date()) + 'fs.stat: ' + JSON.stringify(directory));
                                    }
                                    if (error) {
                                        exists = false;
                                        success();
                                    } else if (!stats.isDirectory()) {
                                        failure('Path ' + JSON.stringify(directory) + ' is not a directory!');
                                    } else {
                                        exists = true;
                                        success();
                                    }
                                });
                            });
                            // Создаем директори, если её не существует.
                            deferred.add(function (success, failure) {
                                var time = new Date();
                                if (exists) {
                                    success();
                                } else {
                                    fs.mkdir(directory, function (error) {
                                        if (DEBUGGER && useDebugging) {
                                            // Выводим отладочную информацию в консоль.
                                            cli.debug(difference(time, new Date()) + 'fs.mkdir: ' + JSON.stringify(directory));
                                        }
                                        if (error) {
                                            failure(error);
                                        } else {
                                            success();
                                        }
                                    });
                                }
                            });
                        }
                    });
                deferred.run();
            },
            // Определяем все возможные файлы для компиляции.
            function (success, failure) {
                var callback, deferred,
                    time = new Date();
                if (DEBUGGER && useDebugging) {
                    // Выводим отладочную информацию в консоль.
                    cli.debug('====> Create list compilation files');
                }
                callback = function (sources, messages, temp, success, failure) {
                    var time = new Date();
                    fs.readdir(sources, function (error, files) {
                        if (DEBUGGER && useDebugging) {
                            // Выводим отладочную информацию в консоль.
                            cli.debug(difference(time, new Date()) + 'fs.readdir: ' + JSON.stringify(sources));
                        }
                        if (error) {
                            failure(error);
                        } else {
                            // Пролистываем все найденные файлы и
                            // подготавливаем список компилируемых файлов.
                            files.forEach(function (filename, index) {
                                (function (sources, messages, temp) {
                                    files[index] = function (success, failure) {
                                        var time = new Date();
                                        // Проверяем каждый найденный файл в директории.
                                        fs.stat(sources, function (error, stat) {
                                            if (DEBUGGER && useDebugging) {
                                                // Выводим отладочную информацию в консоль.
                                                cli.debug(difference(time, new Date()) + 'fs.stat: ' + JSON.stringify(sources));
                                            }
                                            if (error) {
                                                // Показываем ошибку чтения.
                                                failure(error);
                                            } else if (stat.isDirectory()) {
                                                if (!/^[a-z](-?[a-z0-9])*$/.test(filename)) {
                                                    // Показываем ошибку, директории необходимо создавать правильно, по формату.
                                                    failure('Directory name ' + JSON.stringify(sources) + ' does not match the regular expression /^[a-z](-?[a-z0-9])*$/.');
                                                } else {
                                                    // Добавляем в деферред обработку новой директории.
                                                    deferred.add(function (success, failure) {
                                                        callback(
                                                            sources,  // Директория исходных файлов.
                                                            messages, // Директория сообщений переводов.
                                                            temp,     // Директория для временных файлов.
                                                            success,  // Обработчик успешного действия.
                                                            failure   // Обработчик ошибки.
                                                        );
                                                    });
                                                    success();
                                                }
                                            } else if (stat.isFile()) {
                                                if (!/^[a-z](-?[a-z0-9])*\.soy$/.test(filename)) {
                                                    failure('Template name ' + JSON.stringify(sources) + ' does not match the regular expression /^[a-z](-?[a-z0-9])*\\.soy$/.');
                                                } else {
                                                    (function (list) {
                                                        var priorities = [],
                                                            skeleton = sources. // Скелет исходника.
                                                                replace(sourcesDirectory + path.sep, '').
                                                                replace(/\.soy$/i, '');
                                                        // Получаем все пути файлов приоритетов.
                                                        list.forEach(function (element) {
                                                            // Добавляем файл приоритета.
                                                            priorities.push(path.join(messagesDirectory, element,
                                                                path.dirname(temp.replace(tempDirectory + path.sep, '')),
                                                                path.basename(filename, path.extname(filename)) + '.xlf'));
                                                        });
                                                        // Добавляем полные пути.
                                                        filenames.push({
                                                            'sources' : sources,
                                                            'messages' : path.join(messagesDirectory, sourceLocale,
                                                                path.dirname(temp.replace(tempDirectory + path.sep, '')),
                                                                path.basename(filename, path.extname(filename)) + '.xlf'),
                                                            'temp' : path.join(tempDirectory, '~' + path.basename(output, path.extname(output)),
                                                                String(timestamp), path.dirname(temp.replace(tempDirectory + path.sep, '')), filename),
                                                            'priorities' : priorities
                                                        });
                                                        // Составляем скелет исходников.
                                                        if (sourcesSkeleton.indexOf(skeleton) === -1) {
                                                            sourcesSkeleton.push(skeleton);
                                                        }
                                                    }(priorities));
                                                    success();
                                                }
                                            } else {
                                                failure('Path ' + JSON.stringify(sources) + ' is unknown resource.');
                                            }
                                        });
                                    };
                                }(
                                    path.join(sources,  filename), // Директория исходных файлов.
                                    path.join(messages, filename), // Директория сообщений переводов.
                                    path.join(temp,     filename)  // Директория для временных файлов.
                                ));
                            });
                            Deferred.$create(files, function (error) {
                                if (error) {
                                    failure(error['message']);
                                } else {
                                    success();
                                }
                            }).run();
                        }
                    });
                };
                deferred = Deferred.$create([
                    function (success, failure) {
                        callback(
                            sourcesDirectory,  // Директория исходных файлов.
                            messagesDirectory, // Директория сообщений переводов.
                            tempDirectory,     // Директория для временных файлов.
                            success,           // Обработчик успешного действия.
                            failure            // Обработчик ошибки.
                        );
                    }
                ], function (error) {
                    if (DEBUGGER && useDebugging) {
                        // Выводим отладочную информацию в консоль.
                        cli.debug(difference(time, new Date()) + 'Complete!');
                    }
                    // Сортируем скелет исходных файлов.
                    sourcesSkeleton.sort(function (a, b) {
                        if (a < b) { return -1; }
                        if (a > b) { return 1; }
                        return 0;
                    });
                    if (error) {
                        failure(error['message']);
                    } else {
                        success();
                    }
                });
                deferred.run();
            },
            // Принудительно заданный список файлов.
            function (success, failure) {
                var paths = [], // Список существующих шаблонов в обрезанном виде.
                    error = ''; // Текст ошибки.
                // Если список файлов для компиляции
                // не пустой, переопреляем объект обработки.
                if (files.length) {
                    // Вычисляем относительные пути шаблонов.
                    filenames.forEach(function (element) {
                        element = element['sources'].
                            replace(sourcesDirectory + path.sep, ''). // Вычлиняем директорию исходников.
                            replace(/\.soy$/, '');                    // Вычлиняем расширение шаблонов.
                        paths.push(element);
                    });
                    files.forEach(function (element) {
                        if (!error && paths.indexOf(element) === -1) {
                            error = 'File ' + JSON.stringify(path.join(sourcesDirectory, element + '.soy')) + ' not found!';
                        }
                    });
                    if (error) {
                        // Останавливаем процесс с ошибкой.
                        failure(error);
                    } else {
                        filenames = [];
                        // Формируем новый список компилируемых файлов.
                        files.forEach(function (element) {
                            (function (list) {
                                var priorities = [];
                                // Получаем все пути файлов приоритетов.
                                list.forEach(function (locale) {
                                    // Добавляем файл приоритета.
                                    priorities.push(path.join(messagesDirectory,
                                        locale, element + '.xlf'));
                                });
                                // Добавляем полные пути.
                                filenames.push({
                                    'sources' : path.join(sourcesDirectory, element + '.soy'),
                                    'messages' : path.join(messagesDirectory, sourceLocale, element + '.xlf'),
                                    'temp' : path.join(tempDirectory, '~' + path.basename(output, path.extname(output)),
                                        String(Number(timestamp)), element + '.soy'),
                                    'priorities' : priorities
                                });
                            }(priorities));
                        });
                        success();
                    }
                } else {
                    success();
                }
            },
            // Проверяем, что существует файлы для компиляции.
            function (success, failure) {
                if (!filenames.length) {
                    failure('No files to compile.');
                } else {
                    success();
                }
            },
            // Определяем признак кеширования данных, т.е. кеш
            // включается в том случае, если компиляция не нужна.
            function (success, failure) {
                if (DEBUGGER && useDebugging) {
                    // Выводим отладочную информацию в консоль.
                    cli.debug('====> Create sign of caching');
                }
                var time = new Date(),
                    updateTimestamp = 0,
                    deferred = Deferred.$create([], function (error) {
                        if (DEBUGGER && useDebugging) {
                            // Выводим отладочную информацию в консоль.
                            cli.debug(difference(time, new Date()) + 'Complete!');
                        }
                        if (error) {
                            failure(error['message']);
                        } else {
                            success();
                        }
                    });
                // Проверяем время измения каждого файла и определение
                // максимальной временой метки всех файлов.
                filenames.forEach(function (element) {
                    var sources = element['sources'];
                    deferred.add(function (success, failure) {
                        var time = new Date();
                        fs.stat(sources, function (error, stats) {
                            if (DEBUGGER && useDebugging) {
                                // Выводим отладочную информацию в консоль.
                                cli.debug(difference(time, new Date()) + 'fs.stat: ' + JSON.stringify(sources));
                            }
                            if (!error) {
                                // Определяем максимальное время
                                // изменения файлов шаблонов.
                                updateTimestamp = Math.max(updateTimestamp,
                                    Number(stats.mtime) || 0);
                                success();
                            } else {
                                failure(error);
                            }
                        });
                    });
                });
                // Вычисление признака кеша.
                deferred.add(function (success) {
                    var time = new Date();
                    fs.stat(output, function (error, stats) {
                        if (DEBUGGER && useDebugging) {
                            // Выводим отладочную информацию в консоль.
                            cli.debug(difference(time, new Date()) + 'fs.stat: ' + JSON.stringify(output));
                        }
                        if (!error && (Number(stats.mtime) || 0) > updateTimestamp) {
                            cache = true;
                        }
                        success();
                    });
                });
                // Запускаем процесс.
                deferred.run();
            },
            // Создаем директории для временных файлов.
            function (success, failure) {
                if (DEBUGGER && useDebugging) {
                    // Выводим отладочную информацию в консоль.
                    cli.debug('====> Create temporary directories');
                }
                var time = new Date(),
                    directories = [],
                    deferred = Deferred.$create([], function (error) {
                        if (DEBUGGER && useDebugging) {
                            // Выводим отладочную информацию в консоль.
                            cli.debug(difference(time, new Date()) + 'Complete!');
                        }
                        if (error) {
                            failure(error['message']);
                        } else {
                            success();
                        }
                    });
                if (!cache) {
                    // Сортируем файлы в обратном порядке.
                    filenames.sort(function (a, b) {
                        a = a['temp'];
                        b = b['temp'];
                        if (a < b) { return -1; }
                        if (a > b) { return 1; }
                        return 0;
                    });
                    // Собираем уникальные директории для создания,
                    // отбрасываем все поддиректории.
                    filenames.forEach(function (element) {
                        var exists = false;
                        element = path.dirname(element['temp']);
                        directories.forEach(function (directory) {
                            exists = directory.indexOf(element) === 0;
                        });
                        if (directories.indexOf(element) === -1 && !exists) {
                            directories.push(element);
                        }
                    });
                    // Определяем уникальный список создаваемых директорий.
                    (function () {
                        var temp = [];
                        directories.forEach(function (directory) {
                            directory.split(path.sep).forEach(function (element, index, array) {
                                element = array.slice(0, index + 1).join(path.sep);
                                if (element && element.length > tempDirectory.length && temp.indexOf(element) === -1) {
                                    temp.push(element);
                                }
                            });
                        });
                        directories = temp;
                    }());
                    // Перебираем каждую директорию и пытаемся её создать.
                    directories.forEach(function (element) {
                        var exists = true; // Признак существования директории.
                        // Проверяем, что создаваемая директория существует.
                        deferred.add(function (success, failure) {
                            var time = new Date();
                            fs.stat(element, function (error, stats) {
                                if (DEBUGGER && useDebugging) {
                                    // Выводим отладочную информацию в консоль.
                                    cli.debug(difference(time, new Date()) + 'fs.stat: ' + JSON.stringify(element));
                                }
                                if (error) {
                                    exists = false;
                                    success();
                                } else if (!stats.isDirectory()) {
                                    failure('Path ' + element + ' is not a directory!');
                                } else {
                                    success();
                                }
                            });
                        });
                        // Если не существует, то создаем директорию.
                        deferred.add(function (success, failure) {
                            var time = new Date();
                            if (!exists) {
                                // Пытаемся создать директорию.
                                fs.mkdir(element, function (error) {
                                    if (DEBUGGER && useDebugging) {
                                        // Выводим отладочную информацию в консоль.
                                        cli.debug(difference(time, new Date()) + 'fs.mkdir: ' + JSON.stringify(element));
                                    }
                                    if (error) {
                                        failure(error);
                                    } else {
                                        success();
                                    }
                                });
                            } else {
                                success();
                            }
                        });
                    });
                }
                deferred.run();
            },
            // Создаем временные файлы компиляции.
            function (success, failure) {
                if (DEBUGGER && useDebugging) {
                    // Выводим отладочную информацию в консоль.
                    cli.debug('====> Create temporary files');
                }
                var time = new Date(),
                    deferred = Deferred.$create([], function (error) {
                        if (DEBUGGER && useDebugging) {
                            // Выводим отладочную информацию в консоль.
                            cli.debug(difference(time, new Date()) + 'Complete!');
                        }
                        if (error) {
                            failure(error['message']);
                        } else {
                            success();
                        }
                    });
                if (!cache) {
                    filenames.forEach(function (element) {
                        var sources   = element['sources'], // Полный путь к исходному файлу шаблона.
                            temp      = element['temp'],    // Полный путь к временному файлу шаблона.
                            namespace = [],
                            content   = '';
                        // Определяем пространство имен для временного файла.
                        sources.replace(sourcesDirectory + path.sep, '').
                            split(path.sep).forEach(function (element) {
                                element = element.
                                    replace(/\.soy$/i, '').
                                    replace(/\-/g, '_');
                                if (element) {
                                    namespace.push(element);
                                }
                            });
                        // Читаем содержимое исходников.
                        deferred.add(function (success, failure) {
                            var time = new Date();
                            fs.readFile(sources, function (error, value) {
                                var fileContent  = '',
                                    useNamespace = /\{namespace[^\{\}]*\}/,
                                    useCurrent   = new RegExp('\\{namespace\\s+' + namespace.join('\\.') + '[^\\{\\}]*\\}');
                                if (DEBUGGER && useDebugging) {
                                    // Выводим отладочную информацию в консоль.
                                    cli.debug(difference(time, new Date()) + 'fs.readFile: ' + JSON.stringify(sources));
                                }
                                if (error) {
                                    failure(error);
                                } else {
                                    fileContent = value.toString('utf8');
                                    if (useNamespace.test(fileContent) && !useCurrent.test(fileContent)) {
                                        failure('The namespace is not aligned with the template ' + JSON.stringify(sources) + '!');
                                    } else if (!useNamespace.test(fileContent)) {
                                        // Начинаем готовить контент для временных файлов.
                                        content = '// Namespace: ' + namespace.join('.') + '\n' +
                                            '{namespace ' + namespace.join('.') + ' autoescape="contextual"}\n\n' +
                                            fileContent;
                                        success();
                                    }
                                }
                            });
                        });
                        // Сохранение временного файла.
                        deferred.add(function (success, failure) {
                            var time = new Date();
                            fs.writeFile(temp, content, function (error) {
                                if (DEBUGGER && useDebugging) {
                                    // Выводим отладочную информацию в консоль.
                                    cli.debug(difference(time, new Date()) + 'fs.writeFile: ' + JSON.stringify(temp));
                                }
                                if (error) {
                                    failure(error);
                                } else {
                                    success();
                                }
                            });
                        });
                        // Собираем блобальные переменные.
                        deferred.add(function (success) {
                            if (globals.indexOf(namespace[0]) === -1) {
                                globals.push(namespace[0]);
                            }
                            success();
                        });
                    });
                }
                deferred.run();
            },
            // Создаем директории для переводов.
            function (success, failure) {
                if (DEBUGGER && useDebugging) {
                    // Выводим отладочную информацию в консоль.
                    cli.debug('====> Create source directories messages');
                }
                var time = new Date(),
                    directories = [],
                    deferred = Deferred.$create([], function (error) {
                        if (DEBUGGER && useDebugging) {
                            // Выводим отладочную информацию в консоль.
                            cli.debug(difference(time, new Date()) + 'Complete!');
                        }
                        if (error) {
                            failure(error['message']);
                        } else {
                            success();
                        }
                    });
                if (!cache) {
                    // Сортируем файлы в обратном порядке.
                    filenames.sort(function (a, b) {
                        a = a['messages'];
                        b = b['messages'];
                        if (a < b) { return -1; }
                        if (a > b) { return 1; }
                        return 0;
                    });
                    // Собираем уникальные директории для создания,
                    // отбрасываем все поддиректории.
                    filenames.forEach(function (element) {
                        var exists = false;
                        element = path.dirname(element['messages']);
                        directories.forEach(function (directory) {
                            exists = directory.indexOf(element) === 0;
                        });
                        if (directories.indexOf(element) === -1 && !exists) {
                            directories.push(element);
                        }
                    });
                    // Определяем уникальный список создаваемых директорий.
                    (function () {
                        var temp = [];
                        directories.forEach(function (directory) {
                            directory.split(path.sep).forEach(function (element, index, array) {
                                element = array.slice(0, index + 1).join(path.sep);
                                if (element && element.length > messagesDirectory.length && temp.indexOf(element) === -1) {
                                    temp.push(element);
                                }
                            });
                        });
                        directories = temp;
                    }());
                    // Перебираем каждую директорию и пытаемся её создать.
                    directories.forEach(function (directory) {
                        var exists = true; // Признак существования директории.
                        // Проверяем существование директории.
                        deferred.add(function (success, failure) {
                            var time = new Date();
                            fs.stat(directory, function (error, stats) {
                                if (DEBUGGER && useDebugging) {
                                    // Выводим отладочную информацию в консоль.
                                    cli.debug(difference(time, new Date()) + 'fs.stat: ' + JSON.stringify(directory));
                                }
                                if (error) {
                                    exists = false;
                                    success();
                                } else if (!stats.isDirectory()) {
                                    failure('Path ' + JSON.stringify(directory) + ' is not a directory!');
                                } else {
                                    success();
                                }
                            });
                        });
                        // Пытаемся создать директорию, если её нет.
                        deferred.add(function (success, failure) {
                            var time = new Date();
                            if (!exists) {
                                fs.mkdir(directory, function (error) {
                                    if (DEBUGGER && useDebugging) {
                                        // Выводим отладочную информацию в консоль.
                                        cli.debug(difference(time, new Date()) + 'fs.mkdir: ' + JSON.stringify(directory));
                                    }
                                    if (error) {
                                        failure(error);
                                    } else {
                                        success();
                                    }
                                });
                            } else {
                                success();
                            }
                        });
                    });
                }
                deferred.run();
            },
            // Создаем оригинальные файлы переводов.
            function (success, failure) {
                if (DEBUGGER && useDebugging) {
                    // Выводим отладочную информацию в консоль.
                    cli.debug('====> Create source files messages');
                }
                var time = new Date(),
                    deferred  = Deferred.$create([], function (error) {
                        if (DEBUGGER && useDebugging) {
                            // Выводим отладочную информацию в консоль.
                            cli.debug(difference(time, new Date()) + 'Complete!');
                        }
                        if (error) {
                            failure(error['message']);
                        } else {
                            success();
                        }
                    });
                if (!cache) {
                    // Перебираем все файлы и ставим в
                    // очередь на создание локализации.
                    filenames.forEach(function (element) {
                        var messages1 = element['messages'],
                            messages2 = path.join(path.dirname(messages1),
                                '~' + path.basename(messages1)),
                            sources   = element['sources'], // Абсолютный путь к файлу шаблона.
                            temp      = element['temp'],    // Абсолютный путь к временному файлу.
                            modified1 = 0,                  // Время модификации исходного файла шаблона.
                            modified2 = 0,                  // Время модификации исходного файла перевода.
                            content1  = '',                 // Контент основного файла переводв.
                            content2  = '',                 // Контент временного файла переводв.
                            args      = [
                                'java',
                                '-jar',                 closureMsgExtractor, // Бинарник компилятора.
                                '--outputFile',         messages2,           // Абсолютный путь к файлу переводов.
                                '--sourceLocaleString', sourceLocale,        // Локализация текущих исходников.
                                '--allowExternalCalls',                      // Безопасное использование вызовов.
                                '--srcs',               temp                 // Временный файл шаблона.
                            ];
                        // Проверяем время модификации исходного файла шаблона.
                        deferred.add(function (success, failure) {
                            var time = new Date();
                            fs.stat(sources, function (error, stats) {
                                if (DEBUGGER && useDebugging) {
                                    // Выводим отладочную информацию в консоль.
                                    cli.debug(difference(time, new Date()) + 'fs.stat: ' + JSON.stringify(sources));
                                }
                                if (error) {
                                    failure(error);
                                } else {
                                    if (!stats.isFile()) {
                                        failure('Path ' + JSON.stringify(sources) + ' is not a file!');
                                    } else {
                                        modified1 = Number(stats.mtime);
                                        success();
                                    }
                                }
                            });
                        });
                        // Проверяем время модификации файла перефода.
                        deferred.add(function (success, failure) {
                            var time = new Date();
                            fs.stat(messages1, function (error, stats) {
                                if (DEBUGGER && useDebugging) {
                                    // Выводим отладочную информацию в консоль.
                                    cli.debug(difference(time, new Date()) + 'fs.stat: ' + JSON.stringify(messages1));
                                }
                                if (error) {
                                    success();
                                } else {
                                    if (!stats.isFile()) {
                                        failure('Path ' + JSON.stringify(sources) + ' is not a file!');
                                    } else {
                                        modified2 = Number(stats.mtime);
                                        success();
                                    }
                                }
                            });
                        });
                        // Если время модификации исходного файла шаблона больше времени
                        // модификации файла перевода, то делаем извлечение шаблонов.
                        deferred.add(function (success, failure) {
                            var error = '',
                                time = new Date(),
                                command;
                            if (modified1 > modified2) {
                                command = spawn('/usr/bin/env', args);
                                command.stderr.on('data', function (data) {
                                    error += data.toString('utf8') + '\n';
                                });
                                command.on('close', function (code) {
                                    if (code !== 0) {
                                        failure(error);
                                    } else {
                                        if (DEBUGGER && useDebugging) {
                                            // Выводим отладочную информацию в консоль.
                                            cli.debug(difference(time, new Date()) + 'xliff: ' + JSON.stringify(temp) + ' => ' + JSON.stringify(messages2));
                                        }
                                        success();
                                    }
                                });
                            } else {
                                success();
                            }
                        });
                        // Получаем контент оригинального файла переводов.
                        deferred.add(function (success) {
                            var time = new Date();
                            if (modified1 > modified2) {
                                fs.readFile(messages1, function (error, content) {
                                    if (DEBUGGER && useDebugging) {
                                        // Выводим отладочную информацию в консоль.
                                        cli.debug(difference(time, new Date()) + 'fs.readFile: ' + JSON.stringify(messages1));
                                    }
                                    if (!error) {
                                        content1 = content.toString('utf8');
                                    }
                                    success();
                                });
                            } else {
                                success();
                            }
                        });
                        // Получаем контент временного файла переводов.
                        deferred.add(function (success) {
                            var time = new Date();
                            if (modified1 > modified2) {
                                fs.readFile(messages2, function (error, content) {
                                    if (DEBUGGER && useDebugging) {
                                        // Выводим отладочную информацию в консоль.
                                        cli.debug(difference(time, new Date()) + 'fs.readFile: ' + JSON.stringify(messages2));
                                    }
                                    if (!error) {
                                        content2 = content.toString('utf8');
                                    }
                                    success();
                                });
                            } else {
                                success();
                            }
                        });
                        // Переносим извлеченный перевод из временного файла в оригинальный.
                        deferred.add(function (success, failure) {
                            var time = new Date();
                            if (modified1 > modified2 && content1 !== content2) {
                                fs.writeFile(messages1, content2, function (error) {
                                    if (DEBUGGER && useDebugging) {
                                        // Выводим отладочную информацию в консоль.
                                        cli.debug(difference(time, new Date()) + 'fs.writeFile: ' + JSON.stringify(messages1));
                                    }
                                    if (error) {
                                        failure(error);
                                    } else {
                                        success();
                                    }
                                });
                            } else {
                                success();
                            }
                        });
                        // Удаляем временный файл переводов.
                        deferred.add(function (success, failure) {
                            var time = new Date();
                            if (modified1 > modified2) {
                                fs.unlink(messages2, function (error) {
                                    if (DEBUGGER && useDebugging) {
                                        // Выводим отладочную информацию в консоль.
                                        cli.debug(difference(time, new Date()) + 'fs.unlink: ' + JSON.stringify(messages2));
                                    }
                                    if (error) {
                                        failure(error);
                                    } else {
                                        success();
                                    }
                                });
                            } else {
                                success();
                            }
                        });
                    });
                }
                deferred.run();
            },
            // Проверяем, что директория переводов названы как
            // правильные локализации и являются директориями.
            function (success, failure) {
                var time  = new Date(), // Время начала процесса.
                    files = [];         // Список директорий.
                if (DEBUGGER && useDebugging) {
                    // Выводим отладочную информацию в консоль.
                    cli.debug('====> Check directory localizations');
                }
                Deferred.$create([
                    // Получаем список директорий-локализаций.
                    function (success, failure) {
                        var time = new Date(); // Время начала процесса.
                        fs.readdir(messagesDirectory, function (error, filesList) {
                            if (DEBUGGER && useDebugging) {
                                // Выводим отладочную информацию в консоль.
                                cli.debug(difference(time, new Date()) + 'fs.readdir: ' + JSON.stringify(messagesDirectory));
                            }
                            if (error) {
                                failure(error);
                            } else {
                                files = filesList;
                                success();
                            }
                        });
                    },
                    // Проверяем, что в директории локализаций
                    // лежат только директории локализаций.
                    function (success, failure) {
                        var newList = [],
                            deferred = Deferred.$create([], function (error) {
                                if (error) {
                                    failure(error['message']);
                                } else {
                                    success();
                                }
                            });
                        // Добавляем в деферред проверку
                        // каждого пути, что это директория.
                        files.forEach(function (element) {
                            var dirname = path.join(messagesDirectory, element);
                            deferred.add(function (success, failure) {
                                var time = new Date(); // Время начала процесса.
                                fs.stat(dirname, function (error, stats) {
                                    if (DEBUGGER && useDebugging) {
                                        // Выводим отладочную информацию в консоль.
                                        cli.debug(difference(time, new Date()) + 'fs.stat: ' + JSON.stringify(dirname));
                                    }
                                    if (error) {
                                        failure(error);
                                    } else if (!stats.isDirectory()) {
                                        if (useDebugging) {
                                            // Выводим отладочную информацию в консоль.
                                            cli.debug('Path ' + JSON.stringify(dirname) + ' is not a directory!');
                                        }
                                        success();
                                    } else {
                                        newList.push(element);
                                        success();
                                    }
                                });
                            });
                        });
                        files = newList;
                        // Запускаем деферред.
                        deferred.run();
                    },
                    // Проверяем, что название директорий
                    // являются правильными названиями локализаций.
                    function (success, failure) {
                        var error = '', // Текст ошибки.
                            newList = [];
                        files.forEach(function (element) {
                            var dirname = path.join(messagesDirectory, element);
                            if (!/^[a-z]{2,2}\-[A-Z]{2,2}$/.test(element)) {
                                if (useDebugging) {
                                    // Выводим отладочную информацию в консоль.
                                    cli.debug('Directory ' + JSON.stringify(dirname) + ' does not match the regular expression /^[a-z]{2,2}\\-[A-Z]{2,2}$/!');
                                }
                            } else {
                                newList.push(element);
                            }
                        });
                        files = newList;
                        success();
                    }
                ], function (error) {
                    if (DEBUGGER && useDebugging) {
                        // Выводим отладочную информацию в консоль.
                        cli.debug(difference(time, new Date()) + 'Complete!');
                    }
                    if (error) {
                        failure(error['message']);
                    } else {
                        directoriesLocales = files;
                        success();
                    }
                }).run();
            },
            // Поиск лишних файлов локализаций, они уже устарели и необходимо вывести сообщение о том,
            // что переводы стоит удалить или переместить в правильные файлы.
            function (success) {
                if (DEBUGGER && useDebugging) {
                    // Выводим отладочную информацию в консоль.
                    cli.debug('====> Check directory localization');
                }
                var time = new Date(),
                    callback = function (directory, fn) {
                        var time = new Date(); // Время начала процесса.
                        fs.readdir(directory, function (error, files) {
                            if (DEBUGGER && useDebugging) {
                                // Выводим отладочную информацию в консоль.
                                cli.debug(difference(time, new Date()) + 'fs.readdir: ' + JSON.stringify(directory));
                            }
                            var deferred = Deferred.$create([], function (error) {
                                if (error) {
                                    fn(error['message']);
                                } else {
                                    fn();
                                }
                            });
                            if (error) {
                                fn(error);
                            } else {
                                if (useDebugging && !files.length) {
                                    // Выводим отладочную информацию в консоль.
                                    cli.debug('Directory ' + JSON.stringify(directory) + ' is empty!');
                                }
                                files.forEach(function (file) {
                                    deferred.add(function (success, failure) {
                                        var filename = path.join(directory, file),
                                            time = new Date(); // Время начала процесса.
                                        fs.stat(filename, function (error, stats) {
                                            if (DEBUGGER && useDebugging) {
                                                // Выводим отладочную информацию в консоль.
                                                cli.debug(difference(time, new Date()) + 'fs.stat: ' + JSON.stringify(filename));
                                            }
                                            if (error) {
                                                failure(error);
                                            } else if (stats.isDirectory()) {
                                                callback(filename, function (error) {
                                                    if (error) {
                                                        failure(error);
                                                    } else {
                                                        success();
                                                    }
                                                });
                                            } else if (stats.isFile()) {
                                                (function (skeleton) {
                                                    if (sourcesSkeleton.indexOf(skeleton) === -1) {
                                                        if (useDebugging) {
                                                            // Выводим отладочную информацию в консоль.
                                                            cli.debug('File ' + JSON.stringify(filename) + ' is deprecated!');
                                                        }
                                                    }
                                                }(filename.replace(messagesDirectory + path.sep, '').
                                                        split(path.sep).slice(1).join(path.sep).replace(/\.xlf$/, '')));
                                                success();
                                            } else {
                                                if (useDebugging) {
                                                    // Выводим отладочную информацию в консоль.
                                                    cli.debug('Path ' + JSON.stringify(filename) + ' is a unknown resource!');
                                                }
                                                success();
                                            }
                                        });
                                    });
                                });
                                deferred.run();
                            }
                        });
                    },
                    deferred = Deferred.$create([], function (error) {
                        if (DEBUGGER && useDebugging) {
                            // Выводим отладочную информацию в консоль.
                            cli.debug(difference(time, new Date()) + 'Complete!');
                        }
                        if (error && useDebugging) {
                            // Выводим отладочную информацию в консоль.
                            cli.debug(error);
                        } else {
                            success();
                        }
                    });
                // Перебираем все директории локализаций и
                // запускаем процесс поиска устаревших ресурсов.
                directoriesLocales.forEach(function (locale) {
                    deferred.add(function (success, failure) {
                        callback(path.join(messagesDirectory, locale), function (error) {
                            if (error) {
                                failure(error);
                            } else {
                                success();
                            }
                        });
                    });
                });
                deferred.run();
            },
            // Создаем временный файл локализации и попутно показываем сообщения
            // для отладочного режима, если сообщения уже не существует.
            function (success, failure) {
                var xml = [ // Контент для выходного файла перевода.
                        '<?xml version="1.0" encoding="UTF-8"?>',
                        '<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">',
                        '  <file original="SoyMsgBundle" datatype="x-soy-msg-bundle" xml:space="preserve" ' +
                            'source-language="' + sourceLocale + '" target-language="' + targetLocale + '">',
                        '    <body>'
                    ],
                    alreadyUsed        = [], // Список идентификаторов, которые уже использовались.
                    skeletons          = [], // Список каркасов, для того чтобы не делать лишней обработки.
                    identifiers        = [], // Список идентификаторов переводов.
                    identifiersByFiles = {}, // Список идентификаторов переводов сортированный по файлам.
                    time               = new Date(), // Время начала процесса.
                    deferred;
                if (!cache) {
                    if (DEBUGGER && useDebugging) {
                        // Выводим отладочную информацию в консоль.
                        cli.debug('====> Create translation file');
                    }
                    deferred = Deferred.$create([], function (error) {
                        if (DEBUGGER && useDebugging) {
                            // Выводим отладочную информацию в консоль.
                            cli.debug(difference(time, new Date()) + 'Complete!');
                        }
                        if (error) {
                            // Останавливаем процесс с ошибкой.
                            failure(error);
                        } else {
                            success();
                        }
                    });
                    // Собираем идентификаторы переводов
                    // исходных файлов локализаций.
                    filenames.forEach(function (element) {
                        var sources   = element['sources'],
                            messages  = element['messages'],
                            skeleton  = sources.
                                replace(sourcesDirectory + path.sep, '').
                                replace(/\.soy$/, '');
                        // Собираем все идентификаторы переводов,
                        // которые будут использоваться.
                        deferred.add(function (success, failure) {
                            var time = new Date(); // Время начала процесса.
                            fs.readFile(messages, function (error, content) {
                                if (DEBUGGER && useDebugging) {
                                    // Выводим отладочную информацию в консоль.
                                    cli.debug(difference(time, new Date()) + 'fs.readFile: ' + JSON.stringify(messages));
                                }
                                var document, // Документ текущего файла перевода.
                                    elements, // Список элементов перевода.
                                    element,  // Елемент одного перевода.
                                    index,    // Порядковый номер элемента перевода.
                                    id;       // Идентификатор элемента перефода.
                                if (error) {
                                    failure(error);
                                } else {
                                    document = jsdom(content.toString('utf8'));
                                    elements = document.getElementsByTagName('trans-unit');
                                    for (index = 0; index < elements.length; index += 1) {
                                        element = elements[index];
                                        if (element.hasAttribute('id') && element.getAttribute('id')) {
                                            // Определяем идентификатор перевода.
                                            id = String(element.getAttribute('id'));
                                            // Если идентификатора нет списке,
                                            // то добавляем его в список.
                                            if (identifiers.indexOf(id) === -1) {
                                                identifiers.push(id);
                                            }
                                            // Определяем список каркасов, для того,
                                            // чтобы не делать лишней обработки.
                                            if (skeletons.indexOf(skeleton) === -1) {
                                                skeletons.push(skeleton);
                                            }
                                            // Добавляем идентификаторы перефодов по файлам.
                                            if (!(identifiersByFiles[skeleton] instanceof Array)) {
                                                identifiersByFiles[skeleton] = [];
                                            }
                                            if (identifiersByFiles[skeleton].indexOf(id) === -1) {
                                                identifiersByFiles[skeleton].push(id);
                                            }
                                        }
                                    }
                                    success();
                                }
                            });
                        });
                    });
                    // Ищем переводы в файлах других локализаций.
                    filenames.forEach(function (element) {
                        var sources    = element['sources'],
                            priorities = element['priorities'].slice(0),
                            skeleton   = sources.
                                replace(sourcesDirectory + path.sep, '').
                                replace(/\.soy$/, '');
                        deferred.add(function (success, failure) {
                            var deferred = Deferred.$create([], function (error) {
                                if (error) {
                                    // Останавливаем процесс с ошибкой.
                                    failure(error);
                                } else {
                                    success();
                                }
                            });
                            // Перебираем все приоритеты с конца списка и если
                            // переводы нашлись, то прерываем обработку приоритетов.
                            while (priorities.length) {
                                (function () {
                                    // Полный путь к файлу локализации по приоритету, может не существовать.
                                    var priority = priorities.pop();
                                    deferred.add(function (success, failure) {
                                        var document,          // DOM-документ для анализа перевода.
                                            time = new Date(); // Время начала процесса.
                                        fs.readFile(priority, function (error, content) {
                                            var listUsed = [],
                                                skeletonList = identifiersByFiles[skeleton] || [];
                                            if (DEBUGGER && useDebugging) {
                                                // Выводим отладочную информацию в консоль.
                                                cli.debug(difference(time, new Date()) + 'fs.readFile: ' + JSON.stringify(priority));
                                            }
                                            if (error) {
                                                // Ничего не делаем, файла
                                                // локализации не существует.
                                                success();
                                            } else {
                                                document = jsdom(content.toString('utf8'));
                                                skeletonList.forEach(function (id) {
                                                    var index,
                                                        element,
                                                        notes = [],
                                                        target,
                                                        transUnit = document.getElementById(id),
                                                        elements = transUnit.childNodes;
                                                    for (index = 0; index < elements.length; index += 1) {
                                                        element = elements.item(index);
                                                        if (element.tagName === 'TARGET' && typeof target !== 'string') {
                                                            target = String(element.innerHTML || '').
                                                                replace(/\s+/mg, ' ').                     // Удаляем все лишние пробельные символы из перевода.
                                                                replace(/^\s+/, '').                       // Удаляем пробельные символы с начала перевода.
                                                                replace(/\s+$/, '').                       // Удаляем пробельные символы с конца перевода.
                                                                replace(/<x([^>]*)><\/\s*x>/gi, '<x$1/>'); // Управляющие теги делаем унарными.
                                                        }
                                                        if (element.tagName === 'NOTE') {
                                                            notes.push('        ' + String(element.outerHTML).replace(/\n/g, ''));
                                                        }
                                                    }
                                                    if (typeof target === 'string' && alreadyUsed.indexOf(id) === -1) {
                                                        xml.push('      <trans-unit id="' + id + '" datatype="html">');
                                                        xml.push('        <target>' + target + '</target>');
                                                        if (notes.length) {
                                                            xml.push(notes.join('\n'));
                                                        }
                                                        xml.push('      </trans-unit>');
                                                        alreadyUsed.push(id);
                                                        listUsed.push(id);
                                                    }
                                                });
                                                // Ищем не используемые уже переводы в документе.
                                                if (useDebugging) {
                                                    (function (elements) {
                                                        var index = 0,
                                                            element;
                                                        for (index; index < elements.length; index += 1) {
                                                            element = elements.item(index).getAttribute('id');
                                                            if (skeletonList.indexOf(element) === -1) {
                                                                cli.debug('Element #{element} from file #{filename} is deprecated!'.
                                                                    replace(/#\{element\}/g,  JSON.stringify('trans-unit#' + element)).
                                                                    replace(/#\{filename\}/g, JSON.stringify(priority)));
                                                            }
                                                        }
                                                    }(document.getElementsByTagName('trans-unit')));
                                                }
                                                success();
                                            }
                                        });
                                    });
                                }());
                            }
                            deferred.run();
                        });
                    });
                    // Записываем временный файл переводов.
                    deferred.add(function (success, failure) {
                        var time = new Date(), // Время начала процесса.
                            filename = path.join(path.dirname(output),
                                '~' + path.basename(output, path.extname(output)) + '.xlf');
                        // Закрываем контент для xml-файла.
                        xml.push('    </body>');
                        xml.push('  </file>');
                        xml.push('</xliff>');
                        // Записываем временный файл переводов.
                        fs.writeFile(filename,
                            xml.join('\n'), {
                                'encoding' : 'utf8',
                                'mode'     : parseInt('666', 8)
                            }, function (error) {
                                if (DEBUGGER && useDebugging) {
                                    // Выводим отладочную информацию в консоль.
                                    cli.debug(difference(time, new Date()) + 'fs.writeFile: ' + JSON.stringify(filename));
                                }
                                if (error) {
                                    // Останавливаем процесс с ошибкой.
                                    failure(error);
                                } else {
                                    success();
                                }
                            });
                    });
                    deferred.run();
                } else {
                    success();
                }
            },
            // Компилируем файлы шаблонов.
            function (success, failure) {
                var time = new Date(), // Время начала процесса.
                    deferred = Deferred.$create([], function (error) {
                        if (DEBUGGER && useDebugging) {
                            // Выводим отладочную информацию в консоль.
                            cli.debug(difference(time, new Date()) + 'Complete!');
                        }
                        if (error) {
                            // Останавливаем процесс с ошибкой.
                            failure(error);
                        } else {
                            success();
                        }
                    }),
                    translate = path.join(path.dirname(output),
                        '~' + path.basename(output, path.extname(output)) + '.xlf'),
                    filename = path.join(path.dirname(output),
                        '~' + path.basename(output, path.extname(output)) + '.js'),
                    templatesCompiler = [
                        'java',
                        '-jar',                    closureTemplates,
                        '--allowExternalCalls',
                        '--shouldGenerateJsdoc',
                        '--codeStyle',             'STRINGBUILDER',
                        '--locales',               targetLocale,
                        '--messageFilePathFormat', translate,
                        '--outputPathFormat',      filename
                    ],
                    javascriptCompiler = [
                        'java',
                        '-jar',                closureCompiler,
                        '--charset',           'UTF-8',
                        '--compilation_level', 'SIMPLE_OPTIMIZATIONS',
                        '--formatting',        'SINGLE_QUOTES',
                        '--warning_level',     'QUIET',
                        '--output_wrapper',    (function () {
                            var content = ['Template.templates={};(function () {%output%;'];
                            globals.forEach(function (element) {
                                content.push('Template.templates.', element, '=', element, ';');
                            });
                            content.push('}());');
                            return content.join('');
                        }()),
                        '--js',                filename,
                        '--js_output_file',    output
                    ],
                    error = '';
                if (!cache) {
                    if (DEBUGGER && useDebugging) {
                        // Выводим отладочную информацию в консоль.
                        cli.debug('====> Compiling templates');
                    }
                    // Компилирование файлов.
                    deferred.add(function (success, failure) {
                        var command;
                        filenames.forEach(function (element) {
                            templatesCompiler.push(element['temp']);
                        });
                        command = spawn('/usr/bin/env', templatesCompiler);
                        command.stderr.on('data', function (data) {
                            error += data.toString('utf8') + '\n';
                        });
                        command.on('close', function (code) {
                            if (DEBUGGER && useDebugging) {
                                // Выводим отладочную информацию в консоль.
                                cli.debug(difference(time, new Date()) + 'spawn: ' + JSON.stringify(filename));
                            }
                            if (code !== 0) {
                                failure(error);
                            } else {
                                success();
                            }
                        });
                    });
                    // Компилирование javascript.
                    deferred.add(function (success, failure) {
                        var command = spawn('/usr/bin/env', javascriptCompiler);
                        command.stderr.on('data', function (data) {
                            error += data.toString('utf8') + '\n';
                        });
                        command.on('close', function (code) {
                            if (DEBUGGER && useDebugging) {
                                // Выводим отладочную информацию в консоль.
                                cli.debug(difference(time, new Date()) + 'spawn: ' + JSON.stringify(output));
                            }
                            if (code !== 0) {
                                failure(error);
                            } else {
                                success();
                            }
                        });
                    });
                    deferred.run();
                } else {
                    success();
                }
            },
            // Удаляем все временные файлы, если разрешено удаление.
            function (success, failure) {
                var time = new Date(), // Время начала процесса.
                    translate = path.join(path.dirname(output),
                        '~' + path.basename(output, path.extname(output)) + '.xlf'),
                    filename = path.join(path.dirname(output),
                        '~' + path.basename(output, path.extname(output)) + '.js'),
                    directories = [],
                    deferred;
                if (useCleaning && !cache) {
                    if (DEBUGGER && useDebugging) {
                        // Выводим отладочную информацию в консоль.
                        cli.debug('====> Cleaning temporary files');
                    }
                    // Сортируем файлы в обратном порядке.
                    filenames.sort(function (a, b) {
                        a = a['temp'];
                        b = b['temp'];
                        if (a < b) { return -1; }
                        if (a > b) { return 1; }
                        return 0;
                    });
                    deferred = Deferred.$create([
                        // Удаляем временный файл перевода.
                        function (success) {
                            var time = new Date(); // Время начала процесса.
                            fs.unlink(translate, function () {
                                if (DEBUGGER && useDebugging) {
                                    // Выводим отладочную информацию в консоль.
                                    cli.debug(difference(time, new Date()) + 'fs.unlink: ' + JSON.stringify(translate));
                                }
                                success();
                            });
                        },
                        // Удаляем временный скомпилированный файл.
                        function (success) {
                            var time = new Date(); // Время начала процесса.
                            fs.unlink(filename, function () {
                                if (DEBUGGER && useDebugging) {
                                    // Выводим отладочную информацию в консоль.
                                    cli.debug(difference(time, new Date()) + 'fs.unlink: ' + JSON.stringify(filename));
                                }
                                success();
                            });
                        }
                    ], function (error) {
                        if (DEBUGGER && useDebugging) {
                            // Выводим отладочную информацию в консоль.
                            cli.debug(difference(time, new Date()) + 'Complete!');
                        }
                        if (error) {
                            // Останавливаем процесс с ошибкой.
                            failure(error);
                        } else {
                            success();
                        }
                    });
                    // Удаляем временный файл шаблонов.
                    filenames.forEach(function (element) {
                        var temp = element['temp'];
                        deferred.add(function (success) {
                            var time = new Date(); // Время начала процесса.
                            fs.unlink(temp, function () {
                                if (DEBUGGER && useDebugging) {
                                    // Выводим отладочную информацию в консоль.
                                    cli.debug(difference(time, new Date()) + 'fs.unlink: ' + JSON.stringify(temp));
                                }
                                success();
                            });
                        });
                    });
                    // Собираем уникальные директории для создания,
                    // отбрасываем все поддиректории.
                    filenames.forEach(function (element) {
                        var exists = false;
                        element = path.dirname(element['temp']);
                        directories.forEach(function (directory) {
                            exists = directory.indexOf(element) === 0;
                        });
                        if (directories.indexOf(element) === -1 && !exists) {
                            directories.push(element);
                        }
                    });
                    // Определяем уникальный список создаваемых директорий.
                    (function () {
                        var temp = [];
                        directories.forEach(function (directory) {
                            directory.split(path.sep).forEach(function (element, index, array) {
                                element = array.slice(0, index + 1).join(path.sep);
                                if (element && element.length > tempDirectory.length && temp.indexOf(element) === -1) {
                                    temp.push(element);
                                }
                            });
                        });
                        directories = temp;
                    }());
                    // Сортируем директории в обратном порядке.
                    directories.sort(function (a, b) {
                        if (a < b) { return 1; }
                        if (a > b) { return -1; }
                        return 0;
                    });
                    // Перебираем все временные файлы и пытаемся их удалить.
                    directories.forEach(function (directory) {
                        deferred.add(function (success) {
                            var time = new Date(); // Время начала процесса.
                            fs.rmdir(directory, function () {
                                if (DEBUGGER && useDebugging) {
                                    // Выводим отладочную информацию в консоль.
                                    cli.debug(difference(time, new Date()) + 'fs.rmdir: ' + JSON.stringify(directory));
                                }
                                success();
                            });
                        });
                    });
                    deferred.run();
                } else {
                    success();
                }
            }
        ], function (error) {
            lock = false; // Снимаем блокировку с процесса компиляции.
            if (typeof callback === 'function') {
                if (error) {
                    // Необходим только текст ошибки.
                    error = error['message'];
                }
                if (DEBUGGER && error) {
                    // Выводим сообщение ошибки
                    // для отладочного режима.
                    cli.fatal(error);
                } else if (DEBUGGER && !error) {
                    // Выводим отладочную информацию в консоль.
                    cli.debug(difference(timestamp, new Date()) + 'Compilation complete!');
                }
                callback(error);
            }
        }).run();

    };

}());

Template.compile({


    'useCleaning'         : false,
    'useDebugging'        : true,
    'sourceLocale'        : 'ru-RU',
    'targetLocale'        : 'en-US',
    'priorities'          : ['de-DE', 'fr-FR', 'en-EN'],

    'output'              : 'temp/templates',
    'files'               : [
//        'defaults/admin/brands',
//        'defaults/admin/models'
    ],

    'baseDirectory'       : __dirname,
    'sourcesDirectory'    : 'views/sources',
    'messagesDirectory'   : 'views/messages',
    'tempDirectory'       : 'temp'
}, function (error) {
    'use strict';
    if (error) {
        console.log('ERROR:', error);
    } else {
        console.log('SUCCESS !!!');
    }
});
